package dxbridge.model.impl;

import java.util.Arrays;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;

import dxbridge.model.IRecord;
import dxbridge.model.ITable;

public class Table implements ITable {

	/**
	 * Constructor of Table instance.
	 * 
	 * @param name
	 *            The name of table.
	 */
	public Table(String name) {
		records = new LinkedList<IRecord>();
		tableName = name;
	}

	/**
	 * Create a table with same structure <b>without</b> records.
	 * 
	 * @param table
	 *            The demo Table.
	 */
	public Table(ITable table) {
		this.tableName = table.getTableName();
		this.fieldNames = table.getFieldNames();
		this.keyIndex = table.getKeyIndex();
		this.foreignKeyMap = table.getForeignKeyMap();
		this.records = new LinkedList<IRecord>();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getKeyFieldNames()
	 */
	public String[] getKeyFieldNames() {
		String[] keyFields = new String[keyIndex.length];
		for (int i = 0; i < keyIndex.length; i++) {
			keyFields[i] = fieldNames[keyIndex[i]];
		}
		return keyFields;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getFieldName(int)
	 */
	public String getFieldName(int index) {
		try {
			return fieldNames[index];
		} catch (ArrayIndexOutOfBoundsException e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getElementName(int)
	 */
	public String getElementName(int index) {
		try {
			return elementNames[index];
		} catch (ArrayIndexOutOfBoundsException e) {
			return null;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getIndexOfField(java.lang.String)
	 */
	public int getIndexOfField(String fieldName) {
		for (int i = 0; i < fieldNames.length; i++) {
			if (fieldNames[i].equals(fieldName)) {
				return i;
			}
		}
		return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getIndexOfElement(java.lang.String)
	 */
	public int getIndexOfElement(String elementName) {
		for (int i = 0; i < fieldNames.length; i++) {
			if (fieldNames[i].equals(elementName)) {
				return i;
			}
		}
		return -1;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getFieldCount()
	 */
	public int getFieldCount() {
		return fieldNames.length;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#insertRecord(model.Record)
	 */
	public void insertRecord(IRecord record) {
		// TODO should check before add, but at current situation , no big deal.
		this.records.add(record);
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getXpath()
	 */
	public String getXpath() {

		if (depth == 0) {
			return new StringBuffer().append("/").append("xmlroot").append("/")
					.append(this.tableName).toString();
		} else {
			return new StringBuffer().append(this.parent.getXpath())
					.append("/").append(tableName).toString();
		}
	}

	/**
	 * Basic field of this table, for debug use.
	 */
	@Override
	public String toString() {
		StringBuffer sb = new StringBuffer();
		sb.append("Table name:").append(this.tableName);
		sb.append(" record :").append(records.size());
		return sb.toString();
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#compareTo(model.Table)
	 */
	@Override
	public int compareTo(ITable arg0) {
		if (this.depth < arg0.getDepth()) {
			return -1;
		} else if (this.depth > arg0.getDepth()) {
			return 1;
		} else {
			return 0;
		}
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getTableNameWithDBName()
	 */
	public String getTableNameWithDBName() {
		return "PUB." + this.tableName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getDepth()
	 */
	public int getDepth() {
		return depth;
	}

	/**
	 * Set {@link #depth} for this table.
	 * 
	 * @param int depth
	 */

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#isIndexKey(int)
	 */
	public boolean isIndexKey(int index) {
		for (int i : this.keyIndex) {
			if (index == i) {
				return true;
			}
		}
		return false;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setDepth(int)
	 */
	public void setDepth(int depth) {
		this.depth = depth;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getRecords()
	 */
	public List<IRecord> getRecords() {
		return records;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setRecords(java.util.List)
	 */
	public void setRecords(List<IRecord> records) {
		this.records = records;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getParent()
	 */
	public ITable getParent() {
		return parent;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setParent(model.ITable)
	 */
	public void setParent(ITable parent) {
		this.parent = parent;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getFieldNames()
	 */
	public String[] getFieldNames() {
		return fieldNames;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setFieldNames(java.lang.String[])
	 */
	public void setFieldNames(String[] fieldNames) {
		this.fieldNames = fieldNames;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getKeyIndex()
	 */
	public int[] getKeyIndex() {
		return keyIndex;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setKeyIndex(int[])
	 */
	public void setKeyIndex(int[] keyIndex) {
		this.keyIndex = keyIndex;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getTableName()
	 */
	public String getTableName() {
		return tableName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setTableName(java.lang.String)
	 */
	public void setTableName(String tableName) {
		this.tableName = tableName;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getForeignKeyMap()
	 */
	public Map<String, String> getForeignKeyMap() {
		return foreignKeyMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setForeignKeyMap(java.util.Map)
	 */
	public void setForeignKeyMap(Map<String, String> foreignKeyMap) {
		this.foreignKeyMap = foreignKeyMap;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getElementNames()
	 */
	public String[] getElementNames() {
		return elementNames;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setElementNames(java.lang.String[])
	 */
	public void setElementNames(String[] elementNames) {
		this.elementNames = elementNames;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#getFieldTypes()
	 */
	public String[] getFieldTypes() {
		return fieldTypes;
	}

	/*
	 * (non-Javadoc)
	 * 
	 * @see model.ITable#setFieldTypes(java.lang.String[])
	 */
	public void setFieldTypes(String[] fieldTypes) {
		this.fieldTypes = fieldTypes;
	}

	public void printTest() {
		/* test for table it self */
		System.out.println(String.format("===== Table self test print ====="));
		System.out.println(String.format("Table name: %s", this.tableName));
		System.out.println(String.format("Table depth: %d", this.getDepth()));
		System.out.println(String.format("Table record count: %d", this.records
				.size()));
		System.out.println(String.format("Table field names: %s", Arrays
				.deepToString(fieldNames)));
		System.out.println(String.format("Table keyField names: %s", Arrays
				.deepToString(getKeyFieldNames())));
		
		if (this.getForeignKeyMap() != null) {
			System.out.println(String.format("Table relations:"));
			for (String key : this.getForeignKeyMap().keySet()) {
				System.out.println(String.format("%s <-> %s", key, this
						.getForeignKeyMap().get(key)));
			}
		}

		/* print test for it's records */
		// for (int i = 0; i < this.records.size(); i++) {
		// records.get(i).printTest();
		// }
	}

	/** The name of this table */
	private String tableName;

	/** The field names of this table */
	private String[] fieldNames;

	/**
	 * The element name in data xml file, should be mapped to
	 * {@link #fieldNames}
	 */
	private String[] elementNames;

	/**
	 * The type of the fields,follow sequence in {@link #fieldNames}
	 */
	private String[] fieldTypes;

	/**
	 * The index of which fields is the key field in {@link #fieldNames}
	 */
	private int[] keyIndex;

	/**
	 * The records contains in this table.
	 */
	private List<IRecord> records;

	/**
	 * A map for this table and it's relation table with their foreign keys
	 */
	private Map<String, String> foreignKeyMap = null;

	/** depth of this table, 0 is root table, and step is 2 */
	private int depth;

	/** The parent table, if any */
	private ITable parent;

}
